﻿using dz.u9.jky.schema.Attrib;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace dz.inner.jky.api
{
    public class ObjectCopyUtil
    {
        private LogUtils logger = new LogUtils();
        public void CopyProperty<S,T>( S source, ref  T taget)
        {
            PropertyInfo[] properties = source.GetType().GetProperties();

            foreach (var property in properties)
            {   
                dynamic value = ReflectionUtility.GetPropertyValue(source, property.Name);
                bool isList = IsGenericType(property);

                if (value == null && isList == false)
                    continue;

                FieldAttrib fieldAttrib = GetAttribute(property);

                if(fieldAttrib.IsIgnore) continue; 

                SetProperty(ref taget, property, fieldAttrib,value);
                
            }
        }

        /// <summary>
        /// 判断类型是否为集合属性
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        private bool IsGenericType(PropertyInfo property)
        {
            return (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(List<>));
        }

        private void SetProperty<T>(ref T taget, PropertyInfo pi, FieldAttrib fieldAttrib, dynamic value)
        {
            switch (fieldAttrib.FieldType)
            {
                case FieldTypeEnum.Normal:
                    SetNormalProperty(ref taget, fieldAttrib.Name, value);
                    break;
                case FieldTypeEnum.EnumObject:
                    SetEnumProperty(ref taget, fieldAttrib, value);
                    break;
                case FieldTypeEnum.SimpleObject:
                    SetSimpleObject(ref taget, fieldAttrib, value);
                    break;
                case FieldTypeEnum.ComplexObject:
                    SetComplexObject(ref taget, fieldAttrib, value);
                    break;
                case FieldTypeEnum.ListObject:
                    SetListProperty(ref taget, fieldAttrib, value);
                    break;

            }
        }


        private void SetComplexObject<T>(ref T taget, FieldAttrib attr, object value)
        {
            object instance = CreateInstance(attr.FullType);

            CopyProperty(value, ref instance);

            ReflectionUtility.SetPropertyValue(taget, attr.Name, instance);
        }

       

        /// <summary>
        /// 设置集合类属性
        /// </summary>
        /// <param name="taget"></param>
        /// <param name="attrib"></param>
        /// <param name="value"></param>
        /// <exception cref="Exception"></exception>
        private void SetListProperty<T>(ref T taget, FieldAttrib attrib,  dynamic value)
        {
            string fullName = attrib.FullType;

            Type type = null;

            try
            {
                type = Type.GetType(fullName);
            }
            catch (Exception ex)
            {
                throw new DzException($"类型{fullName} 加载失败", ex);
            }

            // 创建集合的Type对象，这里以List<>为例
            Type listType = typeof(List<>).MakeGenericType(type);

            //// 使用Activator创建集合的实例
            object listInstance = Activator.CreateInstance(listType);

            //// 将集合实例转换为List<User>
            var lists = (IList)listInstance;



            if (value != null)
            {
                IEnumerable enumerable = (IEnumerable)value;

                foreach (var item in enumerable)
                {
                    try
                    {
                        object instance = Activator.CreateInstance(type);

                        CopyProperty(item, ref instance);

                        lists.Add(instance);
                    }
                    catch (Exception ex)
                    {
                        string s = ex.Message;
                        s = ex.StackTrace;
                        throw new DzException(ex.Message);
                    }
                }
            }
            
            ReflectionUtility.SetPropertyValue(taget, attrib.Name, lists);
        }

        private object CreateInstance(string fullName)
        {
            Type type = Type.GetType(fullName);

            if (type == null) throw new DzException($"类型{fullName} 加载失败");

            try
            {
                return Activator.CreateInstance(type);
            }
            catch (Exception ex)
            {
                throw new DzException($"创建类型{fullName}失败", ex);
            }
        }
        private void SetSimpleObject<T>(ref T taget, FieldAttrib attrib, object value)
        {
            object instance = CreateInstance(attrib.FullType);

            #region 对简单对象中的属性进行赋值
            string name = ((DzSimpleObject)attrib.Attribute).MapName;

            ReflectionUtility.SetPropertyValue(instance, name, value);            
            #endregion

            #region 将简单对象赋值到目标对象
            ReflectionUtility.SetPropertyValue(taget, attrib.Name, instance);           
            #endregion


        }


        /// <summary>
        /// 设置枚举属性值
        /// </summary>
        /// <param name="taget"></param>
        /// <param name="attrib"></param>
        /// <param name="value"></param>
        /// <exception cref="Exception"></exception>
        private void SetEnumProperty<T>(ref T taget, FieldAttrib attrib, dynamic value)
        {
            #region 获取枚举值
            Type t1 = Type.GetType(attrib.FullType);

            MethodInfo mi = t1.GetMethod("GetFromValue", new Type[] { typeof(int) });

            if (mi == null)
            {
                throw new DzException($"{t1.Name} 中未发现方法 GetFromValue");
            }
            dynamic enumValue = mi.Invoke(null, new object[] { (int)value });
            #endregion

            #region 设置枚举值            
            ReflectionUtility.SetPropertyValue(taget, attrib.Name, enumValue);
            #endregion
        }

        /// <summary>
        /// 简单属性设置，适用于普通属性，属性上没有自定义属性
        /// </summary>
        /// <param name="taget"></param>
        /// <param name="pName"></param>
        /// <param name="value"></param>
        private void SetNormalProperty<T>(ref T taget, string pName, dynamic value)
        {
            ReflectionUtility.SetPropertyValue(taget, pName, value);

        }
     



        private FieldAttrib GetAttribute(PropertyInfo propertyInfo)
        {
            FieldAttrib rtn = new FieldAttrib(propertyInfo);            

            object[] attributes = (object[])propertyInfo.GetCustomAttributes(true);

            if (attributes.Length == 0) return rtn;
            

            foreach (object attr in attributes)
            {                
                switch (attr.GetType().Name)
                {
                    case "DzIgnore":
                        rtn.IsIgnore = true;
                        break;
                    case "DzAlias":
                        rtn.Alias = ((DzAlias)attr).Name;
                        break;
                    case "DzSimpleObject":
                        rtn.FieldType = FieldTypeEnum.SimpleObject;
                        rtn.Attribute = (DzSimpleObject)attr;
                        rtn.FullType = ((DzSimpleObject)attr).FullName;                        
                        break;
                    case "DzComplexObject":
                        rtn.FieldType = FieldTypeEnum.ComplexObject;
                        rtn.Attribute = (DzComplexObject)attr;
                        rtn.FullType = ((DzComplexObject)attr).FullName;
                        break;
                    case "DzEnum":
                        rtn.FieldType = FieldTypeEnum.EnumObject;
                        rtn.Attribute = (DzEnum)attr;
                        rtn.FullType = ((DzEnum)attr).FullName;
                        break;
                    case "DzList":
                        rtn.FieldType = FieldTypeEnum.ListObject;
                        rtn.Attribute = (DzList)attr;
                        rtn.FullType = ((DzList)attr).FullName;
                        break;
                    default:
                        throw new DzException($"未处理类型{attributes[0].GetType().Name}");
                }
            }
            return rtn;
        }      
    }
}
